#pragma once
#include "..\math\matrix.h"
#include "..\math\vector.h"

// ================================================================================================
// Basic Camera class
// ================================================================================================
class Camera {

    private: 
        Matrix4x4 mat;              // our camera matrix
        Matrix4x4 transmat;         // transmat
        float xrot;                 // x rotation
        float yrot;                 // y rotation
        Vector4 left;               // left
        Vector4 at;                 // camera look at
        Vector4 up;                 // up directions
        Vector4 pos;                // where the camera is in the world
        bool dirty;                 // do we need to create a new matrix

        void ConstructMatrix();
    public:

        // -- default constructor
        Camera();
        Camera(const Vector4& at, const Vector4& up);
        Camera(const Vector4& at, const Vector4& up, const Vector4& pos);

        // -- mutator methods
        void SetPosition(const Vector4& pos_);
        void Translate(const Vector4& trans);
        void SetLookAt(const Vector4& at_);
        void RotateViewX(float rot);
        void RotateViewY(float rot);

        // -- accessor methods
        Vector4& GetPosition();
        Vector4& GetPosition() const;

        const Matrix4x4& GetCamTransform();
};

// ================================================================================================
// Constructor
// ================================================================================================
inline Camera::Camera() : left(1.0f, 0.0f, 0.0f), at(0.0f, 0.0f, 1.0f),
                          up(0.0f, 1.0f, 0.0f), pos(0.0f, 0.0f, 0.0f), 
                          dirty(false), xrot(0.0f), yrot(0.0f) {
}

inline Camera::Camera(const Vector4& at_, const Vector4& up_) : left(), at(at_), up(up_),
                                                                pos(0.0f, 0.0f, 0.0f),
                                                                dirty(true), xrot(0.0f), 
                                                                yrot(0.0f) {
}

inline Camera::Camera(const Vector4& at_, const Vector4& up_, const Vector4& pos_) : left(),
                                                                                     at(at_),
                                                                                     up(up_),
                                                                                     pos(pos_),
                                                                                     dirty(true),
                                                                                     xrot(0.0f),
                                                                                     yrot(0.0f) {
}

// ================================================================================================
// Accessor Methods
// ================================================================================================
inline const Matrix4x4& Camera::GetCamTransform() {
    if(dirty)  {
        ConstructMatrix();
        dirty = false;
    }

    return transmat;
}

inline Vector4& Camera::GetPosition() {
    return pos;
}

// ================================================================================================
//  Mutator methods
// ================================================================================================
inline void Camera::SetPosition(const Vector4& pos_) {
    pos = pos_;
    dirty = true;
}

inline void Camera::SetLookAt(const Vector4& at_) {
    at = at_;
    dirty = true;
}

inline void Camera::Translate(const Vector4& trans) {
    Vector4 temp = trans * mat;
    pos += temp;
    dirty = true;
}

inline void Camera::RotateViewX(float rot) {
    xrot += rot;
    dirty = true;
}

inline void Camera::RotateViewY(float rot) {
    yrot += rot;
    dirty = true;
}

